/*
 * ========THE SOLMIX PROJECT=====================================
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.gnu.org/licenses/ 
 * or see the FSF site: http://www.fsf.org. 
 */

package org.solmix.fmk.velocity;

import java.util.ArrayList;
import java.util.List;

import org.apache.velocity.app.event.EventCartridge;
import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
import org.apache.velocity.context.Context;
import org.solmix.commons.logs.Logger;
import org.solmix.services.datasource.DataSource;

/**
 * Reference 'Stream insertion' event handler. Called with object that will be inserted into stream via
 * value.toString(). Please return an Object that will toString() nicely :)
 * 
 * @author solomon
 * @since 0.0.1
 * @version $ID$ 2010-12-19 solmix-web-core
 */
public class DSReferenceInsertionEventHandler implements ReferenceInsertionEventHandler
{

   public DSReferenceInsertionEventHandler( Context ctx )
   {
      this( ctx, null, false );
   }

   public DSReferenceInsertionEventHandler( Context ctx, DataSource ds, boolean escapeValues )
   {
      externals = new ArrayList< String >();
      this.escapeValues = escapeValues;
      this.ds = ds;
      EventCartridge ec = new EventCartridge();
      ec.addEventHandler( this );
      ec.attachToContext( ctx );
      Object keys[] = ctx.getKeys();
      for ( int i = 0; i < keys.length; i++ )
         externals.add( keys[i].toString() );

   }

   /**
    * A call-back which is executed during Velocity merge before a reference value is inserted into the output stream.
    * All registered ReferenceInsertionEventHandlers are called in sequence. If no ReferenceInsertionEventHandlers are
    * are registered then reference value is inserted into the output stream as is.
    * <p>
    * note: this javadoc is from velocity javadoc for convenience.
    * 
    * @param reference Reference from template about to be inserted.
    * @param data Value about to be inserted (after its toString() method is called).
    * @return Object on which toString() should be called for output
    */
   public Object referenceInsert( String reference, Object data )
   {
      foundObject = data;
      if ( escapeValues && ds == null )
      {
         log.warn( "getParameter() called but DataSource has not been set - returning warning" );
         return ( new StringBuilder() ).append( "'Unsafe to retrieve " ).append( reference ).append( " - DataSource has not been set'" ).toString();
      }
      if ( escapeValues )
      {
         for ( int i = 0; i < ESCAPING_EXCEPTIONS.length; i++ )
            if ( reference.equals( ESCAPING_EXCEPTIONS[i] )
               || reference.startsWith( ( new StringBuilder() ).append( ESCAPING_EXCEPTIONS[i] ).append( "." ).toString() ) )
            {
               return data;
            }
         boolean escape = false;
         for ( String str : externals )
         {
            String external = ( new StringBuilder() ).append( "$" ).append( str ).toString();
            if ( reference.equals( external ) || reference.startsWith( external + "." ) )
            {
               escape = true;
               break;
            }
         }
         if ( escape )
            return ds.escapeValue( data, reference );
         // return null;
         else
            return data;
      } else
      {
         return data;
      }
   }

   private static final String ESCAPING_EXCEPTIONS[] = { "$defaultSelectClause", "$defaultTableClause", "$defaultWhereClause",
      "$defaultValuesClause", "$defaultGroupClause", "$defaultGroupWhereClause", "$defaultOrderClause", "$rawValue", "$filter", "$equals",
      "$substringMatches", "$fields", "$qfields" };

   private static Logger log = new Logger( DSReferenceInsertionEventHandler.class.getName() );

   private final boolean escapeValues;

   private final DataSource ds;

   private final List< String > externals;

   public Object foundObject;

}
